package com.ruoyi.common.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.ValueFilter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.ruoyi.common.exception.BaseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.util.StringUtils;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.*;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.*;

public class JavaBeanUtil {

    private static final Logger logger = LoggerFactory.getLogger(JavaBeanUtil.class);

    private JavaBeanUtil() {
    }


    public static JSONObject fastjsonToJSON(Object bean) {
        JSONObject json = null;
        if (bean != null) {
            try {
                String jsonStr = JSON.toJSONString(bean,
                        SerializerFeature.WriteNullListAsEmpty);
                json = JSON.parseObject(jsonStr);
            } catch (Exception e) {
                logger.error("parseBeanToJson error", e);
            }
        }
        return json;
    }

    /**
     * 对象类型转换, 返回指定类的新对象
     *
     * @param source 输入对象
     * @param clazz  输出对象类型
     * @param <T>    泛型标识
     * @return 转换后的对象
     * @throws BaseException 对象类型转换失败
     */
    public static <T> T transferTo(Object source, Class<T> clazz) throws BaseException {
        T dest = null;
        if (source != null) {
            try {
                dest = clazz.newInstance();
                copyProperties(dest, source);
            } catch (InstantiationException | IllegalAccessException e) {
                logger.error("transferTo异常", e);
                throw new BaseException("对象类型转换失败");
            }
        }
        return dest;
    }


    public static Object copyProperties(Object dest, Object ori) {
        if (ori == null) {
            return null;
        }
        try {
//            Object tmp = deepClone(ori);
            BeanUtils.copyProperties(ori, dest);
        } catch (Exception e) {
            logger.error("copyProperties error", e);
            dest = null;
        }
        return dest;
    }

    public static String map2Str(Map<String, String> map, String kvSep, String recSep) {
        StringBuilder stringBuilder = new StringBuilder();
        for (Map.Entry<String, String> entry : map.entrySet()) {
            stringBuilder.append(entry.getKey()).append(kvSep).append(entry.getValue()).append(recSep);
        }
        return stringBuilder.substring(0, stringBuilder.lastIndexOf(recSep));

    }

    public static boolean isAllEmpty(Object bean, Collection<String> attrCollect) {
        // 提取bean中所有非空属性
        JSONObject validAttr = getValidParams(bean);
        trimEmpty(validAttr);
        // 计算bean非空属性与需要校验属性的交集
        Set<String> interSet = new HashSet<>(validAttr.keySet());
        interSet.retainAll(attrCollect);
        return interSet.isEmpty();
    }

    /**
     * 去除输入json中值为空的键
     *
     * @param input 待trim的输入
     */
    private static void trimEmpty(JSONObject input) {
        if (input != null) {
            try {
                input.entrySet().removeIf(temp -> temp.getValue() == null || isStrEmpty(temp.getValue().toString()));
            } catch (Exception e) {
                logger.error("去除空值键名出现异常", e);
                throw new BaseException("去除空值键名出现异常");
            }
        }
    }

    // 利用反射校验对象值
    public static boolean validateAttrs(Object bean, Map<String, Object> attrKvMap) {
        List<String> emptyAttrList = validateAttrs2(bean, attrKvMap);
        return emptyAttrList.isEmpty();
    }

    private static final String EXCEPTION_ENCOUNTER = "exception encountered";

    /**
     * 对象属性校验，返回值为null的属性名列表
     *
     * @param bean      待校验的对象
     * @param attrKvMap 待校验的对象属性Map<attr, defaultValueIfNull>
     * @return 校验未通过的属性列表
     */
    public static List<String> validateAttrs2(Object bean, Map<String, Object> attrKvMap) {
        Set<String> emptyAttrSet = new HashSet<>();
        if (bean == null) {
            emptyAttrSet.add("校验对象为空");
        } else {
            try {
                if (bean instanceof Map) {
                    // 按照Map进行校验
                    emptyAttrSet = validateMap((Map<String, Object>) bean, attrKvMap);
                } else {
                    // 按照java对象进行校验
                    emptyAttrSet = validateBean(bean, attrKvMap);
                }
            } catch (Exception e) {
                logger.error("validateAttrs2 error", e);
                emptyAttrSet.add(EXCEPTION_ENCOUNTER);
            }
        }
        return new ArrayList<>(emptyAttrSet);
    }

    // 以反射形式校验java对象
    private static Set<String> validateBean(Object bean, Map<String, Object> attrKvMap) {
        Set<String> emptyAttrSet = new HashSet<>();
        try {
            Class beanClazz = bean.getClass();
            String classFullName = beanClazz.getName();
            String classShortName = classFullName.substring(classFullName.lastIndexOf('.') + 1);
            BeanInfo beanInfo = Introspector.getBeanInfo(beanClazz);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            Set<String> attrNameList = attrKvMap.keySet();

            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
                if (attrNameList.contains(key)) {
                    boolean thisValid = true;
                    Method getter = property.getReadMethod();
                    Object value = getter.invoke(bean);
                    StringBuilder msg = new StringBuilder();
                    if (value == null) {
                        msg.append(classShortName + "." + key + " is null");
                        thisValid = false;
                    } else {
                        Class attrType = property.getPropertyType();
                        if (attrType == String.class && value.equals("")) {
                            msg.append(classShortName + "." + key + " is empty string");
                            thisValid = false;
                        }
                    }
                    if (!thisValid) {
                        Object defaultValue = attrKvMap.get(key);
                        if (defaultValue == null) {
                            emptyAttrSet.add(key);
                        } else {
                            Method setter = property.getWriteMethod();
                            setter.invoke(bean, defaultValue);
                            msg.append(", use default value " + getter.invoke(bean));
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error(EXCEPTION_ENCOUNTER, e);
            emptyAttrSet.add(EXCEPTION_ENCOUNTER);
        }
        return emptyAttrSet;
    }

    // Map类型对象校验
    private static Set<String> validateMap(Map<String, Object> map, Map<String, Object> attrKvMap) {
        Set<String> emptyAttrSet = new HashSet<>(attrKvMap.keySet());
        // 获取属性map中的默认值
        JSONObject defaultParams = getValidParams(attrKvMap);
        for (Map.Entry<String, Object> entry : attrKvMap.entrySet()) {
            String key = entry.getKey();
            Object value = map.get(key);
            if (value == null) {
                value = defaultParams.get(entry.getKey());
            }
            if (value != null) {
                map.put(key, value);
                emptyAttrSet.remove(entry.getKey());
            }
        }
        return emptyAttrSet;
    }

    public static Object deepClone(Object bean) {
        Object target = null;
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(bean);
            // 将流序列化成对象
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            target = ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            logger.error("{}", e);
        }
        return target;
    }

    /**
     * 获取bean中属性值非空的属性json
     *
     * @param bean
     * @return
     */
    public static JSONObject getValidParams(Object bean) {
        JSONObject json = new JSONObject();
        ValueFilter filter = (Object object, String name, Object value)-> {
            if (value != null && value instanceof String) {
                if (!StringUtils.hasText((String) value)) {
                    return null;
                }
            } return value;
        };
        try {
            json = JSON.parseObject(JSON.toJSONString(bean,filter));
        } catch (Exception e) {
            logger.warn("parseBean2JSON failed", e);
        }
        return json;
    }

    private static List<Class<?>> parseGenericType(Type type) {
        List<Class<?>> rootList = new ArrayList<Class<?>>();
        if (type instanceof ParameterizedType) {
            ParameterizedType pType = (ParameterizedType) type;
            rootList.add((Class<?>) pType.getRawType());
            for (Type at : pType.getActualTypeArguments()) {
                List<Class<?>> childList = parseGenericType(at);
                rootList.addAll(childList);
            }
        } else {
            rootList.add((Class<?>) type);
        }
        return rootList;
    }

    public static <T> T fastToBean(JSONObject json, Class<T> clazz) {
        if (json == null) {
            return null;
        }
        return JSON.toJavaObject(json, clazz);
    }

    public static <T> T fastToBean(String jsonStr, Class<T> clazz) {
        if (StringUtils.isEmpty(jsonStr)) {
            return null;
        }
        JSONObject json = JSON.parseObject(jsonStr);
        return JSON.toJavaObject(json, clazz);
    }

    public static <T> T jacksonToBean(String source, Class<T> clazz) {
        return jacksonToBean(source, clazz, null);
    }

    public static <T, E> T jacksonToBean(String source, Class<T> clazz, Class<E> eClass) {
        if (source == null) {
            return null;
        }
        T dest = null;
        try {
            ObjectMapper mapper = new ObjectMapper();
            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            mapper.setDateFormat(new SimpleDateFormat(DateUtils.YYYY_MM_DD_HH_MM_SS, Locale.CHINA));
            if (eClass == null) {
                dest = mapper.readValue(source, clazz);
            } else {
                JavaType javaType = mapper.getTypeFactory().constructParametricType(clazz, eClass);
                dest = mapper.readValue(source, javaType);
            }

        } catch (Exception e) {
            logger.error("transferTo error", e);
        }
        return dest;
    }

    public static String jacksonToString(Object bean) {
        String jsonStr = null;
        try {
            ObjectMapper mapper = new ObjectMapper();
            mapper.configure(SerializationFeature.CLOSE_CLOSEABLE, true);
            mapper.setDateFormat(new SimpleDateFormat(DateUtils.YYYY_MM_DD_HH_MM_SS, Locale.CHINA));
            jsonStr = mapper.writeValueAsString(bean);
        } catch (JsonProcessingException e) {
            logger.error("parseBeanToJsonStr error", e);
        }
        return jsonStr;
    }

    public static <T> T parseToBean(Object source, Class<T> clazz) throws BaseException {
        T dest = null;
        if (source != null) {
            try {
                dest = clazz.newInstance();
                copyProperties(dest, source);
            } catch (InstantiationException | IllegalAccessException e) {
                logger.error("parseTo error", e);
                throw new BaseException("对象转换失败");
            }
        }
        return dest;
    }


    public static boolean isStrEmpty(String str) {
        return str == null || str.trim().isEmpty() || str.trim().equals("null");
    }

}
